/*
 * Runs through a bunch of created DiffdumpOut objects, calculates the rotation
 * ranges, and writes all the results to an XML dataset file using JAXB
 */
package monash.med.crystallography.DataSetTools.DiffdumpXML;

import com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl;
import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
import datasets.jaxb.Datasets;
import datasets.jaxb.ObjectFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.GregorianCalendar;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

/**
 *
 * @author steve
 */
public class DiffdumpXML extends NamespacePrefixMapper {
    
    ArrayList<DiffdumpOut> ddo = new ArrayList<DiffdumpOut>();
    ArrayList<RotationRange> rr = new ArrayList<RotationRange>();
    
    private JAXBContext jaxbContext;
    private ObjectFactory objFactory;
    Marshaller marshaller;
    private Datasets datasets;
    private Datasets.Dataset dataset;
    private ArrayList<Datasets.Dataset.Files.File> filesAdd = new ArrayList<Datasets.Dataset.Files.File>();
    private Float resolution;    
    private String crystalName;
    private String dsName;
    private String diffractometerType;
    private String xraySource;
    private Float mosaicSpread;
    private Float chiAngle;

    public DiffdumpXML() {
        try{
            jaxbContext = JAXBContext.newInstance("datasets.jaxb");
        }
        catch(JAXBException j)
        {
            j.printStackTrace();
        }
        objFactory = new ObjectFactory();  
                
        datasets = objFactory.createDatasets();

        dataset = objFactory.createDatasetsDataset();
        
    }
 
    public void extractMetadata(String imageDir)
    {       
        try{
        File directory = new File(imageDir);
        System.out.println("Scanning images in " + imageDir);
        ddo = ingestDirectory(directory);
        rr = calculateRotationRanges(ddo);
        
        marshaller = jaxbContext.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT ,
               new Boolean(true));        
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
    
    public String writeXMLDataset(String writeDir) {               
        
        //System.out.println(System.getProperty("os.name"));
        //System.out.println(System.getProperty("os.arch"));
        
        if(ddo.size() > 0)
        {
            DiffdumpOut dd = ddo.get(0);
            
            try{
                System.out.println("Writing xml...");

                dataset.setDatasetName(dsName);

                if(dd.getCollectionDate() != null)
                {
                    GregorianCalendar gc = new GregorianCalendar();
                    gc.setTime(dd.getCollectionDate());

                    dataset.setCollectionDate(new XMLGregorianCalendarImpl(gc));                
                }
                
                dataset.setResolutionLimit(resolution); // user input
                
                dataset.setImageType(dd.getImageType());
                dataset.setImages(new BigInteger(String.valueOf(ddo.size()))); // computed

                RotationRange prevr = new RotationRange(0, 0);
                for(RotationRange r : rr)
                {
                    // only unique rotation ranges
                    if(!((r.getStart() == prevr.getStart()) && r.getEnd() == prevr.getEnd()))
                    {                       
                        Datasets.Dataset.OscillationRange o = new Datasets.Dataset.OscillationRange();
                        
                        o.setStart((float)r.getStart());
                        o.setEnd((float)r.getEnd());
                        
                        dataset.getOscillationRange().add(o);
                    }
                    prevr = r;
                }

                dataset.setOscillationAngle((float)dd.getInterval());
                dataset.setDiffractometerType(diffractometerType);
                dataset.setXraySource(xraySource);

                dataset.setCrystalName(crystalName);

                Datasets.Dataset.Files files = objFactory.createDatasetsDatasetFiles();
 
                for(Datasets.Dataset.Files.File file : filesAdd)
                {
                    files.getFile().add(file);
                }

                dataset.setFiles(files);

                dataset.setExposureTime((float)dd.getExposureTime());
                dataset.setDetectorSN(dd.getDetectorsn());
                dataset.setXrayWavelength((float)dd.getWavelength());
                dataset.setDirectBeamXPos((float)dd.getBeamCenterX());
                dataset.setDirectBeamYPos((float)dd.getBeamCenterY());
                dataset.setDetectorDistance((float)dd.getDistanceToDetector());
                dataset.setImageSizeX((float)dd.getImageSizeX());
                dataset.setImageSizeY((float)dd.getImageSizeY());
                dataset.setPixelSizeX((float)dd.getPixelSizeX());
                dataset.setPixelSizeY((float)dd.getPixelSizeY());
                dataset.setTwoTheta((float)dd.getTwoThetaValue());

                if(mosaicSpread != null)
                {
                    dataset.setMosaicSpread(mosaicSpread);
                }
                
                if(chiAngle != null)
                {
                    dataset.setChiAngle(chiAngle);
                }

                datasets.getDataset().add(dataset);

                try {
                    marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", this);
                    marshaller.marshal(datasets,
                    new FileOutputStream(writeDir)); 
                    System.out.println("Wrote extracted/calculated metadata to " + writeDir);
                    System.out.println("from " + ddo.size() + " diffraction images");
                    return writeDir;
                }
                catch(FileNotFoundException f)
                {
                    f.printStackTrace();
                }

            }
            catch(JAXBException j)
            {
                j.printStackTrace();
            }  
        }
        
        return "Error";

    }
    
    /*public static String WriteXML()
    {
        
    }*/
    
    private static ArrayList ingestDirectory(File directory) throws Exception
    {
        System.out.println("Extracting metadata from image files...");
        ArrayList<DiffdumpOut> ddo = new ArrayList<DiffdumpOut>();
        File[] files = directory.listFiles();
        
        for(File file : files)
        {
            if(file.isFile())
            {
                //System.out.println("Scanning... " + file.getName());
                try{
                    ddo.add(new DiffdumpOut(file.getAbsolutePath()));
                }
                catch(Exception e) // shutup! :D (for files that aren't read properly by diffdump)
                {
                    e.printStackTrace();
                }
            }
        }
        
        return ddo;
    }
    
    private static ArrayList<RotationRange> calculateRotationRanges(ArrayList<DiffdumpOut> ddo)
    {
        Collections.sort(ddo, new rotationRangeComparator());
        boolean firstValue = true;
        double initialValue = 0;
        double interval = 0;
        double previousValue = 0;
        ArrayList<RotationRange> rotationRanges = new ArrayList<RotationRange>();
        
        // run through each range detect wedges
        for(DiffdumpOut d : ddo)
        {
            
            if(firstValue)
            {
                initialValue = d.getOscillationStart();
                previousValue = d.getOscillationEnd();
                interval = d.getInterval();
                
                firstValue = false;
            }
            else
            {
                if(d.getOscillationEnd() != (previousValue + interval))
                {
                    rotationRanges.add(new RotationRange(initialValue, previousValue));
                    initialValue = d.getOscillationStart();
                }

                previousValue = d.getOscillationEnd();
            }
        }

        rotationRanges.add(new RotationRange(initialValue, previousValue));
        return rotationRanges;
    }

    public void setFilesAdd(ArrayList<Datasets.Dataset.Files.File> filesAdd) {
        this.filesAdd = filesAdd;
    }

    public void setCrystalName(String crystalName) {
        this.crystalName = crystalName;
    }

    public void setDsName(String dsName) {
        this.dsName = dsName;
    }

    public void setMosaicSpread(float mosaicSpread) {
        this.mosaicSpread = mosaicSpread;
    }

    public void setChiAngle(float chiAngle) {
        this.chiAngle = chiAngle;
    }
    
    public void setResolutionLimit(float resolution) {
        this.resolution = resolution;
    }

    public void setDiffractometerType(String diffractometerType) {
        this.diffractometerType = diffractometerType;
    }

    public void setXraySource(String xraySource) {
        this.xraySource = xraySource;
    }

    @Override
    public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
        return "oai_ds";
    }
    
}